package org.swingDemo;

import com.formdev.flatlaf.FlatIntelliJLaf;

import javax.swing.*;
import javax.swing.text.MaskFormatter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.text.ParseException;

public class TextInputDemo extends JPanel implements ActionListener, FocusListener {

    JTextField streetField, cityField;
    JFormattedTextField zipField;
    JSpinner stateSpinner;
    boolean addressSet = false;
    Font regularFont, italicFont;
    JLabel addressDisplay;
    final static int GAP = 10;

    public TextInputDemo() {
        setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

        JPanel leftHalf = new JPanel() {
            @Override
            public Dimension getMaximumSize() {
                Dimension pref = getPreferredSize();
                return new Dimension(Integer.MAX_VALUE, pref.height);
            }
        };
        leftHalf.setLayout(new BoxLayout(leftHalf, BoxLayout.PAGE_AXIS));

        leftHalf.add(createEntryFields());
        leftHalf.add(createButtons());

        add(leftHalf);
        add(createAddressDisplay());
    }

    protected JComponent createButtons() {
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.TRAILING));

        JButton button = new JButton("Set address");
        button.addActionListener(this);
        panel.add(button);

        button = new JButton("Clear address");
        button.addActionListener(this);
        button.setActionCommand("clear");
        panel.add(button);

        panel.setBorder(BorderFactory.createEmptyBorder(0, 0, GAP - 5, GAP - 5));
        return panel;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if ("clear".equals(e.getActionCommand())) {
            addressSet = false;
            streetField.setText("");
            cityField.setText("");

            zipField.setValue(null);
        } else {
            addressSet = true;
        }
        updateDisplays();
    }

    protected void updateDisplays() {
        addressDisplay.setText(formatAddress());
        if (addressSet) {
            addressDisplay.setFont(regularFont);
        } else {
            addressDisplay.setFont(italicFont);
        }
    }

    protected JComponent createAddressDisplay() {
        JPanel panel = new JPanel(new BorderLayout());
        addressDisplay = new JLabel();
        addressDisplay.setHorizontalAlignment(JLabel.CENTER);
        regularFont = addressDisplay.getFont().deriveFont(Font.PLAIN, 16.0f);
        italicFont = regularFont.deriveFont(Font.ITALIC);
        updateDisplays();

        panel.setBorder(BorderFactory.createEmptyBorder(GAP / 2, 0, GAP / 2, 0));
        panel.add(new JSeparator(JSeparator.VERTICAL), BorderLayout.LINE_START);
        panel.add(addressDisplay, BorderLayout.CENTER);
        panel.setPreferredSize(new Dimension(200, 150));
        return panel;
    }

    protected String formatAddress() {
        if (!addressSet) {
            return "No address set.";
        }

        String street = streetField.getText();
        String city = cityField.getText();
        String state = (String)stateSpinner.getValue();
        String zip = zipField.getText();
        String empty = "";

        if ((street == null) || empty.equals(street)) {
            street = "<em>(no street specified)</em>";
        }
        if ((city == null) || empty.equals(city)) {
            city = "<em>(no city specified)</em>";
        }
        if ((state == null) || empty.equals(state)) {
            state = "<em>(no state specified)</em>";
        } else {
            int abbrevIndex = state.indexOf('(') + 1;
            state = state.substring(abbrevIndex, abbrevIndex + 2);
        }
        if ((zip == null) || empty.equals(zip)) {
            zip = "";
        }

        StringBuffer sb = new StringBuffer();
        sb.append("<html><p align=center>");
        sb.append(street);
        sb.append("<br>");
        sb.append(city);
        sb.append(" ");
        sb.append(state); //should format
        sb.append(" ");
        sb.append(zip);
        sb.append("</p></html>");

        return sb.toString();
    }

    protected MaskFormatter createFormatter(String s) {
        MaskFormatter formatter = null;
        try {
            formatter = new MaskFormatter(s);
        } catch (ParseException ex) {
            System.err.println("formatter is bad: " + ex.getMessage());
            System.exit(-1);
        }
        return formatter;
    }

    @Override
    public void focusGained(FocusEvent e) {
        Component c = e.getComponent();
        if (c instanceof JFormattedTextField) {
            selectItLater(c);
        } else if (c instanceof JTextField) {
            ((JTextField)c).selectAll();
        }
    }

    protected void selectItLater(Component c) {
        if (c instanceof JFormattedTextField) {
            final JFormattedTextField ftf = (JFormattedTextField)c;
            SwingUtilities.invokeLater(() -> {
                ftf.selectAll();
            });
        }
    }

    @Override
    public void focusLost(FocusEvent e) { }

    protected JComponent createEntryFields() {
        JPanel panel = new JPanel(new SpringLayout());

        String[] labelStrings = {
                "Street address: ",
                "City: ",
                "State: ",
                "Zip code: "
        };

        JLabel[] labels = new JLabel[labelStrings.length];
        JComponent[] fields = new JComponent[labelStrings.length];
        int fieldNum = 0;

        streetField = new JTextField();
        streetField.setColumns(20);
        fields[fieldNum++] = streetField;

        cityField = new JTextField();
        cityField.setColumns(20);
        fields[fieldNum++] = cityField;

        String[] stateStrings = getStateStrings();
        stateSpinner = new JSpinner(new SpinnerListModel(stateStrings));
        fields[fieldNum++] = stateSpinner;

        zipField = new JFormattedTextField(createFormatter("#####"));
        fields[fieldNum++] = zipField;

        for (int i = 0; i < labelStrings.length; i++) {
            labels[i] = new JLabel(labelStrings[i], JLabel.TRAILING);
            labels[i].setLabelFor(fields[i]);
            panel.add(labels[i]);
            panel.add(fields[i]);

            JTextField tf = null;
            if (fields[i] instanceof JSpinner) {
                tf = getTextField((JSpinner)fields[i]);
            } else {
                tf = (JTextField)fields[i];
            }
            tf.addActionListener(this);
            tf.addFocusListener(this);
        }
        SpringUtilities.makeCompactGrid(panel, labelStrings.length, 2,
                GAP, GAP, GAP, GAP / 2);
        return panel;
    }

    public String[] getStateStrings() {
        String[] stateStrings = {
                "Alabama (AL)",
                "Alaska (AK)",
                "Arizona (AZ)",
                "Arkansas (AR)",
                "California (CA)",
                "Colorado (CO)",
                "Connecticut (CT)",
                "Delaware (DE)",
                "District of Columbia (DC)",
                "Florida (FL)",
                "Georgia (GA)",
                "Hawaii (HI)",
                "Idaho (ID)",
                "Illinois (IL)",
                "Indiana (IN)",
                "Iowa (IA)",
                "Kansas (KS)",
                "Kentucky (KY)",
                "Louisiana (LA)",
                "Maine (ME)",
                "Maryland (MD)",
                "Massachusetts (MA)",
                "Michigan (MI)",
                "Minnesota (MN)",
                "Mississippi (MS)",
                "Missouri (MO)",
                "Montana (MT)",
                "Nebraska (NE)",
                "Nevada (NV)",
                "New Hampshire (NH)",
                "New Jersey (NJ)",
                "New Mexico (NM)",
                "New York (NY)",
                "North Carolina (NC)",
                "North Dakota (ND)",
                "Ohio (OH)",
                "Oklahoma (OK)",
                "Oregon (OR)",
                "Pennsylvania (PA)",
                "Rhode Island (RI)",
                "South Carolina (SC)",
                "South Dakota (SD)",
                "Tennessee (TN)",
                "Texas (TX)",
                "Utah (UT)",
                "Vermont (VT)",
                "Virginia (VA)",
                "Washington (WA)",
                "West Virginia (WV)",
                "Wisconsin (WI)",
                "Wyoming (WY)"
        };
        return stateStrings;
    }

    public JFormattedTextField getTextField(JSpinner spinner) {
        JComponent editor = spinner.getEditor();
        if (editor instanceof JSpinner.DefaultEditor) {
            return ((JSpinner.DefaultEditor)editor).getTextField();
        } else {
            System.err.println("Unexpected editor type: " + spinner.getEditor().getClass()
                    + " isn't a descendant of DefaultEditor");
            return null;
        }
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("TextInputDemo");
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(new TextInputDemo());
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(new FlatIntelliJLaf());
                createAndShowGUI();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        });
    }
}
